home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / mg / src.lzh / amiga / ttymenu.c < prev    next >
C/C++ Source or Header  |  1990-05-23  |  20KB  |  764 lines

  1. /*
  2.  * ttymenu.c
  3.  * 
  4.  * Incorporates the browser, for rummaging around on disks, and the usual Emacs
  5.  * editing command menu
  6.  * 
  7.  * Copyright (c) 1986, Mike Meyer Mic Kaczmarczik did a few things along the
  8.  * way.
  9.  * 
  10.  * Permission is hereby granted to distribute this program, so long as this
  11.  * source file is distributed with it, and this copyright notice is not
  12.  * removed from the file.
  13.  * 
  14.  */
  15.  
  16. #include "do_menu.h"
  17. #ifdef    DO_MENU
  18.  
  19. #include <exec/types.h>
  20. #include <libraries/dos.h>
  21. #include <libraries/dosextens.h>
  22. #include <intuition/intuition.h>
  23. #ifdef LATTICE
  24. #include <proto/dos.h>
  25. #include <proto/intuition.h>
  26. #include <proto/exec.h>
  27. #else
  28. #include <functions.h>
  29. #endif
  30.  
  31. #undef    MANX
  32. #undef LATTICE
  33. #undef    AZTEC
  34. #include "compiler.h"
  35. #include "no_macro.h"
  36. #include "no_dired.h"
  37. #include "gosmacs.h"
  38. #include "browser.h"
  39. #include "change_font.h"
  40. #include "menu.h"
  41.  
  42. #undef    TRUE
  43. #undef    FALSE
  44. #include "def.h"
  45.  
  46. #ifdef    BROWSER
  47. #include "buffer.h"
  48. #endif
  49.  
  50. #include "window.h"
  51.  
  52. #ifdef BROWSER
  53. static int Add_Dir PROTO((char *dir, char *name));
  54. static void Add_Devices PROTO((ULONG devtype));
  55. #endif
  56.  
  57. extern struct Menu *AutoMenu;
  58. extern struct Window *EmW;
  59.  
  60. #define MNUM(menu,item,sub) (SHIFTMENU(menu)|SHIFTITEM(item)|SHIFTSUB(sub))
  61.  
  62. #ifdef    BROWSER
  63. #define LONGEST_NAME    80    /* Longest file name we can deal with     */
  64.  
  65. # ifdef    ANSI
  66. #include <string.h>
  67. #include <stdlib.h>
  68. # else
  69. char           *index();    /* find first instance of c in s     */
  70. #define    strchr(s, c) index(s, c)
  71. # endif
  72.  
  73. # ifdef    MENU
  74. #define    FIRSTMENU    1
  75. # else
  76. #define    FIRSTMENU    0
  77. # endif
  78.  
  79. #endif
  80.  
  81. #ifdef    MENU
  82. /*
  83.  * When ttgetc() sees a menu selection event, it stuffs the sequence KMENU
  84.  * <menu><item><subitem> into the input buffer
  85.  * 
  86.  * The menu item names are chosen to be relatively close to the extended
  87.  * function names, so a user can usually figure out the key binding of a menu
  88.  * item by searching through the "display-bindings" buffer for something
  89.  * that's close.
  90.  */
  91.  
  92. /*
  93.  * Commands for managing files and buffers
  94.  */
  95.  
  96. extern int      filevisit();
  97. extern int      poptofile();
  98. extern int      fileinsert();
  99. extern int      filesave();
  100. extern int      filewrite();
  101. #ifndef    NO_DIRED
  102. extern int      dired();
  103. #endif
  104. extern int      usebuffer();
  105. extern int      poptobuffer();
  106. extern int      killbuffer();
  107. extern int      listbuffers();
  108. extern int      savebuffers();
  109. extern int      quit();
  110.  
  111. static struct MenuBinding FileItems[] = {
  112.     {"Find File         C-x C-f", filevisit},
  113.     {"Pop To File       C-x 4 f", poptofile},
  114.     {"Insert File       C-x i", fileinsert},
  115.     {"Save File         C-x C-s", filesave},
  116.     {"Write File        C-x C-w", filewrite},
  117. #ifndef    NO_DIRED
  118.     {"Dired         C-x d", dired},
  119. #endif
  120.     {"Switch To Buffer  C-x b", usebuffer},
  121.     {"Pop To Buffer     C-x 4 b", poptobuffer},
  122.     {"Kill Buffer       C-x k", killbuffer},
  123.     {"List Buffers      C-x C-b", listbuffers},
  124.     {"Save Buffers      C-x s", savebuffers},
  125.     {"Save And Exit     C-x C-c", quit}
  126. };
  127.  
  128. /*
  129.  * Commands for various editing functions
  130.  */
  131.  
  132. extern int      yank();
  133. extern int      openline();
  134. extern int      killline();
  135. extern int      deblank();
  136. extern int      justone();
  137. extern int      indent();
  138. extern int      twiddle();
  139. extern int      quote();
  140.  
  141. static struct MenuBinding EditItems[] = {
  142.     {"Yank                 C-y", yank},
  143.     {"Blank Line           C-o ", openline},
  144.     {"Kill Line            C-k", killline},
  145.     {"Delete Blank Lines   C-x C-o", deblank},
  146.     {"Delete Blanks        M-SPC", justone},
  147.     {"Newline And Indent   C-j", indent},
  148.     {"Transpose Characters C-t", twiddle},
  149.     {"Quoted Insert        C-q", quote}
  150. };
  151.  
  152. /*
  153.  * Movement commands
  154.  */
  155.  
  156. extern int      forwpage();
  157. extern int      backpage();
  158. extern int      gotobol();
  159. extern int      gotobob();
  160. extern int      gotoeol();
  161. extern int      gotoeob();
  162. extern int      gotoline();
  163. extern int      showcpos();
  164.  
  165. static struct MenuBinding MoveItems[] = {
  166.     {"Scroll Up       C-v", forwpage},
  167.     {"Scroll Down     M-v", backpage},
  168.     {"Start Of Line   C-a", gotobol},
  169.     {"Start Of Buffer M-<", gotobob},
  170.     {"End Of Line     C-e", gotoeol},
  171.     {"End Of Buffer   M->", gotoeob},
  172.     {"Goto Line", gotoline},
  173.     {"Show Cursor     C-x =", showcpos}
  174. };
  175.  
  176. /*
  177.  * Commands for searching and replacing
  178.  */
  179.  
  180. extern int      forwisearch();
  181. extern int      backisearch();
  182. extern int      searchagain();
  183. extern int      forwsearch();
  184. extern int      backsearch();
  185. extern int      queryrepl();
  186.  
  187. static struct MenuBinding SearchItems[] = {
  188.     {"I-Search Forward  C-s", forwisearch},
  189.     {"I-Search Backward C-r", backisearch},
  190.     {"Search Again", searchagain},
  191.     {"Search Forward    M-s", forwsearch},
  192.     {"Search Backward   M-r", backsearch},
  193.     {"Query Replace     M-%", queryrepl}
  194. };
  195.  
  196. /*
  197.  * Commands that manipulate words
  198.  */
  199. extern int      forwword();
  200. extern int      backword();
  201. extern int      delfword();
  202. extern int      delbword();
  203. extern int      capword();
  204. extern int      lowerword();
  205. extern int      upperword();
  206.  
  207. static struct MenuBinding WordItems[] = {
  208.     {"Forward Word       M-f", forwword},
  209.     {"Backward Word      M-b", backword},
  210.     {"Kill Word          M-d", delfword},
  211.     {"Backward Kill Word M-DEL", delbword},
  212.     {"Capitalize Word    M-c", capword},
  213.     {"Downcase Word      M-l", lowerword},
  214.     {"Upcase Word        M-u", upperword}
  215. };
  216.  
  217. /*
  218.  * Commands relating to paragraphs
  219.  */
  220. extern int      gotoeop();
  221. extern int      gotobop();
  222. extern int      fillpara();
  223. extern int      setfillcol();
  224. extern int      killpara();
  225. extern int      fillmode();
  226.  
  227. static struct MenuBinding ParaItems[] = {
  228.     {"Forward Paragraph  M-]", gotoeop},
  229.     {"Backward Paragraph M-[", gotobop},
  230.     {"Fill Paragraph     M-q", fillpara},
  231.     {"Set Fill Column    C-x f", setfillcol},
  232.     {"Kill Paragraph", killpara},
  233.     {"Auto Fill Mode", fillmode}
  234. };
  235.  
  236. /*
  237.  * Region stuff
  238.  */
  239. extern int      setmark();
  240. extern int      swapmark();
  241. extern int      killregion();
  242. extern int      copyregion();
  243. extern int      lowerregion();
  244. extern int      upperregion();
  245.  
  246. static struct MenuBinding RegionItems[] = {
  247.     {"Set Mark            C-@", setmark},
  248.     {"Exch Point And Mark C-x C-x", swapmark},
  249.     {"Kill Region         C-w", killregion},
  250.     {"Copy Region As Kill M-w", copyregion},
  251.     {"Downcase Region     C-x C-l", lowerregion},
  252.     {"Upcase Region       C-x C-u", upperregion}
  253. };
  254.  
  255. /*
  256.  * Commands for manipulating windows
  257.  */
  258.  
  259. extern int      splitwind();
  260. extern int      delwind();
  261. extern int      onlywind();
  262. extern int      nextwind();
  263. #ifdef    GOSMACS
  264. extern int      prevwind();
  265. #endif
  266. extern int      enlargewind();
  267. extern int      shrinkwind();
  268. extern int      refresh();
  269. extern int      reposition();
  270. extern int      togglewindow();
  271. #ifdef    CHANGE_FONT
  272. extern int      setfont();
  273. #endif
  274.  
  275. static struct MenuBinding WindowItems[] = {
  276.     {"Split Window         C-x 2", splitwind},
  277.     {"Delete Window        C-x 0", delwind},
  278.     {"Delete Other Windows C-x 1", onlywind},
  279.     {"Next Window          C-x o", nextwind},
  280. #ifdef    GOSMACS
  281.     {"Up Window", prevwind},
  282. #endif
  283.     {"Enlarge Window       C-x ^", enlargewind},
  284.     {"Shrink Window", shrinkwind},
  285.     {"Redraw Display", refresh},
  286.     {"Recenter             C-l", reposition},
  287.     {"Toggle Border", togglewindow},
  288. #ifdef    CHANGE_FONT
  289.     {"Set Font", setfont}
  290. #endif
  291. };
  292.  
  293. /*
  294.  * Miscellaneous commands
  295.  */
  296.  
  297. extern int      definemacro();
  298. extern int      finishmacro();
  299. extern int      executemacro();
  300. extern int      extend();
  301. extern int      bindtokey();
  302. extern int      desckey();
  303. extern int      wallchart();
  304. extern int      showversion();
  305. extern int      spawncli();
  306.  
  307. static struct MenuBinding MiscItems[] = {
  308. #ifndef    NO_MACRO
  309.     {"Start Kbd Macro   C-x (", definemacro},
  310.     {"End Kbd Macro     C-x )", finishmacro},
  311.     {"Call Kbd Macro    C-x e", executemacro},
  312. #endif
  313.     {"Execute Command   M-x", extend},
  314.     {"Global Set Key", bindtokey},
  315.     {"Describe Key      C-h c", desckey},
  316.     {"Describe Bindings C-h b", wallchart},
  317.     {"Emacs Version", showversion},
  318.     {"New CLI           C-z", spawncli}
  319. };
  320.  
  321. /*
  322.  * The following table contains the titles, number of items, and pointers to,
  323.  * the individual menus.
  324.  */
  325.  
  326. static struct MenuInfo EMInfo[] = {
  327.     {"File  ", NITEMS(FileItems), &FileItems[0]},
  328.     {"Edit  ", NITEMS(EditItems), &EditItems[0]},
  329.     {"Move  ", NITEMS(MoveItems), &MoveItems[0]},
  330.     {"Search  ", NITEMS(SearchItems), &SearchItems[0]},
  331.     {"Word  ", NITEMS(WordItems), &WordItems[0]},
  332.     {"Paragraph  ", NITEMS(ParaItems), &ParaItems[0]},
  333.     {"Region  ", NITEMS(RegionItems), &RegionItems[0]},
  334.     {"Window  ", NITEMS(WindowItems), &WindowItems[0]},
  335.     {"Miscellaneous  ", NITEMS(MiscItems), &MiscItems[0]}
  336. };
  337.  
  338. /*
  339.  * There are three cases to deal with; the menu alone, the Browser alone, and
  340.  * both of them together.  We #define some things to make life a little
  341.  * easier to deal with
  342.  */
  343. # ifdef    BROWSER
  344. #  define Edit_Menu_Init() Menu_Add("Edit ", TRUE, FALSE)
  345. #  define Edit_Menu_Add(n) Menu_Item_Add(n,(USHORT)ITEMENABLED,0L,(BYTE)0,FALSE)
  346. #  define Edit_Item_Add(n) Menu_SubItem_Add(n,(USHORT)ITEMENABLED,0L,(BYTE)0,FALSE)
  347. # else
  348. #  define Edit_Menu_Init() cinf = NULL    /* harmless */
  349. #  define Edit_Menu_Add(n) n[strlen(n)-1] = '\0'; Menu_Add(n, TRUE, FALSE)
  350. #  define Edit_Item_Add(n) Menu_Item_Add(n,(USHORT)ITEMENABLED,0L,(BYTE)0,FALSE)
  351. # endif                /* BROWSER */
  352.  
  353. #endif
  354.  
  355. /*
  356.  * Initialize the Emacs menu
  357.  */
  358.  
  359. struct Menu    *
  360. InitEmacsMenu(EmW)
  361.     struct Window  *EmW;
  362. {
  363. #ifdef    MENU
  364.     register struct MenuInfo *cinf;
  365.     register struct MenuBinding *lastbinding, *cb;
  366.     struct MenuInfo *lastinfo;
  367. #endif
  368.  
  369.     Menu_Init();        /* init the menu         */
  370.  
  371. #ifdef    MENU
  372.     Edit_Menu_Init();    /* Set up for editing menu     */
  373.     lastinfo = &EMInfo[NITEMS(EMInfo)];    /* loop sentinel     */
  374.     for (cinf = EMInfo; cinf < lastinfo; cinf++) {
  375.         Edit_Menu_Add(cinf->Name);
  376.         lastbinding = &cinf->Items[cinf->NumItems];
  377.         for (cb = cinf->Items; cb < lastbinding; cb++)
  378.             Edit_Item_Add(cb->Command);
  379.     }
  380. #endif
  381.  
  382. #ifdef    BROWSER
  383.     Menu_Add("Disks ", TRUE, FALSE);    /* name is already there */
  384.     Add_Devices(DLT_DEVICE);/* devices first */
  385.     Add_Devices(DLT_VOLUME);/* mounted volume names next */
  386.     Add_Devices(DLT_DIRECTORY);    /* assigned directories last */
  387. #endif
  388.     return AutoMenu;
  389. }
  390.  
  391. /*
  392.  * amigamenu() -- handles a menu pick.
  393.  */
  394.  
  395. amigamenu(f, n)
  396. {
  397.     unsigned short  menunum, itemnum, subnum, Menu_Number;
  398.     char           *name;
  399. #ifdef    MENU
  400.     register int     (*fp) PROTO((int, int));
  401. #endif
  402.  
  403. #ifdef    BROWSER
  404.     register unsigned short level, i, dirp;
  405.     register char  *cp;
  406.     int             stat;
  407.     struct MenuItem *ItemAddress();
  408.  
  409.     /* State variables that describe the current directory */
  410.     static char     Dir_Name[LONGEST_NAME];
  411.     static unsigned short Menu_Level = 0;
  412. #endif
  413.  
  414.     /* read the menu, item, and subitem codes from the input stream */
  415.     menunum = getkey(DISSCR) - MN_OFFSET;
  416.     itemnum = getkey(DISSCR) - MN_OFFSET;
  417.     subnum = getkey(DISSCR) - MN_OFFSET;
  418.     Menu_Number = (USHORT)
  419.         (SHIFTMENU(menunum) | SHIFTITEM(itemnum) | SHIFTSUB(subnum));
  420.  
  421. #ifndef    BROWSER
  422. # ifdef    MENU
  423.     fp = EMInfo[menunum].Items[itemnum].Function;
  424.     return (*(fp) (f, n));
  425. # endif
  426. #else                /* we're using the Browser */
  427. # ifdef    MENU
  428.     /* Handle commands from the Edit menu when using the Browser */
  429.     if (0 == menunum) {
  430.         fp = EMInfo[itemnum].Items[subnum].Function;
  431.         return ((*fp) (f, n));
  432.     }
  433. # endif
  434.     /* Here when a selection was made in a Browser menu */
  435.     name = (char *) ((struct IntuiText *)
  436.              (ItemAddress(AutoMenu, (ULONG) Menu_Number)->ItemFill))
  437.         ->IText;
  438.     level = MENUNUM(Menu_Number) - FIRSTMENU;
  439.  
  440.     /* Got what we want, so clear the menu to avoid confusing the user */
  441.     ClearMenuStrip(EmW);
  442.  
  443.     /* set dirp to FALSE if the name is not a directory or disk */
  444.     dirp = (strchr(name, '/') != NULL || strchr(name, ':') != NULL);
  445.  
  446.     /* First, set the directory name right */
  447.     if (level > Menu_Level)    /* Not possible, die */
  448.         panic("impossible menu_level in amigamenu");
  449.     else if (level == 0)    /* picked a new disk */
  450.         Dir_Name[0] = '\0';
  451.     else if (level < Menu_Level) {    /* Throw away some levels */
  452.         for (i = 1, cp = strchr(Dir_Name, ':'); i < level; i++) {
  453.             if (cp == NULL)
  454.                 return FALSE;
  455.             cp = strchr(cp, '/');
  456.         }
  457.         if (cp == NULL)
  458.             panic("broken file name in amigamenu");
  459.         *++cp = '\0';
  460.     }
  461.     /* else Menu_Level == level, chose a file a current level */
  462.  
  463.     /* Now, fix up the menu and it's state variable */
  464.     while (Menu_Level > level) {
  465.         Menu_Level--;
  466.         Menu_Pop();
  467.     }
  468.  
  469.     /*
  470.      * If we added a file, visit it, else add a new directory level to
  471.      * the menu.
  472.      */
  473.     if (!dirp)
  474.         stat = Display_File(Dir_Name, name);
  475.     else {
  476.         Menu_Level++;
  477.         (void) strncat(Dir_Name, name,
  478.                    LONGEST_NAME - strlen(Dir_Name) - 1);
  479.         stat = Add_Dir(Dir_Name, name);
  480.     }
  481.     SetMenuStrip(EmW, AutoMenu);
  482.     return stat;
  483. #endif
  484. }
  485.  
  486. #ifdef    BROWSER
  487. /*
  488.  * Display_File - Go fetch a the requested file into a window.
  489.  */
  490. Display_File(dir, file)
  491.     char           *dir, *file;
  492. {
  493.     register struct buffer *bp, *findbuffer();
  494.     char            File_Name[LONGEST_NAME], *fn, *adjustname();
  495.  
  496.     if (!dir || !file)
  497.         return FALSE;
  498.     (void) strcpy(File_Name, dir);
  499.     (void) strncat(File_Name, file, LONGEST_NAME - strlen(File_Name) - 1);
  500.     fn = adjustname(File_Name);
  501.     if ((bp = findbuffer(fn)) == NULL)
  502.         return FALSE;
  503.     curbp = bp;
  504.     if (showbuffer(bp, curwp, WFHARD) != TRUE)
  505.         return FALSE;
  506.     if (bp->b_fname[0] == 0)
  507.         return (readin(fn));    /* Read it in.     */
  508.     return TRUE;
  509. }
  510.  
  511. struct name_node {
  512.     char           *name;
  513.     struct name_node *next;
  514. };
  515. static void free_name_list PROTO((struct name_node * name_list));
  516.  
  517. static void 
  518. free_name_list(name_list)
  519.     register struct name_node *name_list;
  520. {
  521.     register struct name_node *tmp;
  522.  
  523.     while (name_list) {
  524.         tmp = name_list;
  525.         name_list = name_list->next;
  526.         free(tmp->name);
  527.         free(tmp);
  528.     }
  529. }
  530.  
  531. /*
  532.  * Add_Dir - given a dir and a name, add the menu name with the files in dir
  533.  * as entries.  Use AllocMem() in order to make sure the file info block is
  534.  * on a longword boundary.
  535.  */
  536. static
  537. Add_Dir(dir, name)
  538.     char           *dir, *name;
  539. {
  540.     register char  *last_char;
  541.     BPTR            my_lock;
  542.     unsigned short  count;
  543.     int             stat = FALSE;
  544.     static char     Name_Buf[LONGEST_NAME];
  545.     struct FileInfoBlock *File_Info;
  546.     struct name_node *name_list, *tmp_name;
  547.     char           *cp, **name_table;
  548.     int             i;
  549.  
  550.     if ((File_Info = (struct FileInfoBlock *)
  551.          AllocMem((LONG) sizeof(struct FileInfoBlock), 0L)) == NULL)
  552.         return (FALSE);
  553.  
  554.     /* Fix up the trailing / if it needs it */
  555.     last_char = &dir[strlen(dir) - 1];
  556.     if (*last_char == '/')
  557.         *last_char = '\0';
  558.  
  559.     /* Now, start on the directory */
  560.     if ((my_lock = Lock(dir, ACCESS_READ)) == NULL)
  561.         goto out;
  562.  
  563.     if (!Examine(my_lock, File_Info))
  564.         goto out;
  565.     if (File_Info->fib_DirEntryType < 0L)
  566.         goto out;
  567.  
  568.     if (Menu_Add(name, TRUE, TRUE) == 0)
  569.         goto out;
  570.     name_list = NULL;
  571.     for (count = 0; ExNext(my_lock, File_Info)
  572.          || IoErr() != ERROR_NO_MORE_ENTRIES; count++) {
  573.         (void) strcpy(Name_Buf, File_Info->fib_FileName);
  574.         if (File_Info->fib_DirEntryType >= 0L) {
  575.             (void) strcat(Name_Buf, "/");
  576.         }
  577.         if (!(cp = malloc(strlen(Name_Buf) + 1))) {
  578.             free_name_list(name_list);
  579.             return (FALSE);
  580.         }
  581.         strcpy(cp, Name_Buf);
  582.         if (!(tmp_name = (struct name_node *)
  583.               malloc(sizeof(struct name_node)))) {
  584.             free_name_list(name_list);
  585.             return (FALSE);
  586.         }
  587.         tmp_name->name = cp;
  588.         tmp_name->next = name_list;
  589.         name_list = tmp_name;
  590.     }
  591.     if (count > 0) {
  592.         if (!(name_table = (char **) malloc(count * sizeof(char *)))) {
  593.             free_name_list(name_list);
  594.             return (FALSE);
  595.         }
  596.         i = count;
  597.         for (tmp_name = name_list; tmp_name;
  598.              tmp_name = tmp_name->next) {
  599.             name_table[--i] = tmp_name->name;
  600.         }
  601.         tqsort(name_table, count);
  602.         for (i = 0; i < count; i++) {
  603.             if (Menu_Item_Add(name_table[i],
  604.                    (USHORT) ITEMENABLED, 0L, (BYTE) 0, TRUE)
  605.                 == MNUM(NOMENU, NOITEM, NOSUB))
  606.                 break;
  607.         }
  608.         free_name_list(name_list);
  609.         free(name_table);
  610.     } else
  611.         Menu_Item_Add("EMPTY", (USHORT) 0, 0L, (BYTE) 0, FALSE);
  612.  
  613.     /* Put everything back */
  614.     if (*last_char == '\0')
  615.         *last_char = '/';
  616.     stat = TRUE;
  617. out:
  618.     UnLock(my_lock);
  619.     FreeMem(File_Info, (LONG) sizeof(struct FileInfoBlock));
  620.     return stat;
  621. }
  622.  
  623. /*
  624.  * Add all the devices currently known by the system to the current menu,
  625.  * based on the type of device list entry desired.  Disable multitasking
  626.  * while we look inside the device list, so we don't fly off into space while
  627.  * traversing it.
  628.  */
  629. struct DosLibrary *DosBase;
  630.  
  631. static          VOID
  632. Add_Devices(devtype)
  633.     ULONG           devtype;
  634. {
  635.     register struct DeviceList *devlist;
  636.     struct RootNode *rootnode;
  637.     struct DosInfo *dosinfo;
  638.     UBYTE           buffer[80];
  639.     int             ramflag = 0;
  640.     struct name_node *name_list, *tmp_name;
  641.     char           *cp, **name_table;
  642.     int             count, i;
  643.  
  644.     /* if you've gotten this far, none of these will be null. */
  645.     DosBase = (struct DosLibrary *) OpenLibrary(DOSNAME, 0L);
  646.  
  647.     Forbid();        /* let's be careful out there... */
  648.     rootnode = (struct RootNode *) DosBase->dl_Root;
  649.     dosinfo = (struct DosInfo *) BADDR(rootnode->rn_Info);
  650.     devlist = (struct DeviceList *) BADDR(dosinfo->di_DevInfo);
  651.  
  652.     name_list = NULL;
  653.     count = 0;
  654.     while (devlist) {
  655.         /* select by specified device type */
  656.         if (devlist->dl_Type != devtype) {
  657.             devlist = (struct DeviceList *) BADDR(devlist->dl_Next);
  658.             continue;
  659.         }
  660.         /* convert device's name into AmigaDOS name and concat a ":" */
  661.         btocstr((BPTR) devlist->dl_Name, buffer, sizeof(buffer));
  662.         strcat(buffer, ":");
  663.  
  664.         /*
  665.          * Always add volumes and assigned directories. However,
  666.          * disks should be the only devices added to the list. Magic
  667.          * disk test courtesy of Phillip Lindsay, Commodore-Amiga
  668.          * Inc.
  669.          */
  670.         if (devtype != DLT_DEVICE || devlist->dl_Task) {
  671.             count++;
  672.             if (!(cp = malloc(strlen(buffer) + 1))) {
  673.                 free_name_list(name_list);
  674.                 Permit();
  675.                 CloseLibrary((struct Library *) DosBase);
  676.                 return;
  677.             }
  678.             strcpy(cp, buffer);
  679.             if (!(tmp_name = (struct name_node *)
  680.                   malloc(sizeof(struct name_node)))) {
  681.                 free_name_list(name_list);
  682.                 Permit();
  683.                 CloseLibrary((struct Library *) DosBase);
  684.                 return;
  685.             }
  686.             tmp_name->name = cp;
  687.             tmp_name->next = name_list;
  688.             name_list = tmp_name;
  689.             if (devtype == DLT_DEVICE &&
  690.                 !strcmp(buffer, "RAM:"))
  691.                 ramflag = 1;
  692.         }
  693.         devlist = (struct DeviceList *) BADDR(devlist->dl_Next);
  694.     }
  695.     Permit();
  696.     CloseLibrary((struct Library *) DosBase);
  697.     if (count > 0 || (devtype == DLT_DEVICE && !ramflag)) {
  698.         if (!(name_table = (char **)
  699.               malloc((count + 1) * sizeof(char *)))) {
  700.             free_name_list(name_list);
  701.             return;
  702.         }
  703.         name_table[count] = "RAM:";
  704.         i = count;
  705.         for (tmp_name = name_list; tmp_name;
  706.              tmp_name = tmp_name->next) {
  707.             name_table[--i] = tmp_name->name;
  708.         }
  709.         if (devtype == DLT_DEVICE && !ramflag)
  710.             count++;
  711.         tqsort(name_table, count);
  712.         for (i = 0; i < count; i++) {
  713.             if (Menu_Item_Add(name_table[i],
  714.                    (USHORT) ITEMENABLED, 0L, (BYTE) 0, TRUE)
  715.                 == MNUM(NOMENU, NOITEM, NOSUB))
  716.                 break;
  717.         }
  718.         free_name_list(name_list);
  719.         free(name_table);
  720.     }
  721. }
  722.  
  723. btocstr(bp, buf, bufsiz)
  724.     BPTR            bp;
  725.     char           *buf;
  726.     int             bufsiz;
  727. {
  728.     register UBYTE *cp;
  729.     register int    len, i;
  730.  
  731.     cp = (UBYTE *) BADDR(bp);
  732.     len = (int) *(cp++);
  733.     len = (len > bufsiz) ? bufsiz : len;    /* truncate if necessary */
  734.     for (i = 0; i < len; i++)
  735.         buf[i] = *(cp++);
  736.     buf[i] = '\0';
  737.     return (len < bufsiz);    /* return FALSE if truncated */
  738. }
  739.  
  740. #ifndef LATTICE
  741. tqsort(v, n)
  742.     register char **v;
  743.     int             n;
  744. {
  745.     register char  *temp;
  746.     register int    gap, j, i;
  747.  
  748.     for (gap = n / 2; gap > 0; gap /= 2)
  749.         for (i = gap; i < n; i++)
  750.             for (j = i - gap; j >= 0; j -= gap)
  751.                 if (strcmp(v[j], v[j + gap]) > 0) {
  752.                     /* exchange them */
  753.                     temp = v[j];
  754.                     v[j] = v[j + gap];
  755.                     v[j + gap] = temp;
  756.                 }
  757. }
  758. #endif
  759. #endif
  760.  
  761. #else                /* DO_MENU */
  762. #include "nullfile.h"
  763. #endif
  764.